using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
using System.Runtime.Serialization;

namespace Decav.Data
{
    /// <summary>
    /// Contains methods that assist in XML serialization.
    /// </summary>
    public static class XmlSerializationHelper
    {
        /// <summary>
        /// Builds an object of the specified type from an XML reader.
        /// </summary>
        /// <param name="type">The type of object to create.</param>
        /// <param name="reader">The XML reader to read from.</param>
        /// <returns>The fully constructed object.</returns>
        /// <exception cref="SerializationException">Thrown when the object fails to deserialize.</exception>
        public static object BuildObject(Type type, XmlReader reader)
        {
            if (type == null)
                throw new ArgumentNullException("type");

            if (reader == null)
                throw new ArgumentNullException("reader");

            ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(XmlReader) });

            if (ctor == null)
            {
                XmlSerializer serializer = new XmlSerializer(type);
                
                try
                {
                    return serializer.Deserialize(reader);
                }
                catch(Exception ex)
                {
                    throw new SerializationException(string.Format("The object type '{0}' could not deserialize from XML because it does not define a valid constructor 'ctor(XmlReader reader)', and the XmlSerializer cannot deserialize the object.", type.FullName), ex);
                }
            }

            try
            {
                return ctor.Invoke(new object[] { reader });
            }
            catch (SerializationException ex)
            {
                throw new SerializationException(string.Format("An exception occured in the constructor while attempting to deserialize type '{0}'.", type.FullName), ex);
            }
        }

        /// <summary>
        /// Builds an object of the specified type from an XML reader.
        /// </summary>
        /// <param name="reader">The XML reader to read from.</param>
        /// <returns>The fully constructed object.</returns>
        /// <typeparam name="TType">The type of object to build.</typeparam>
        /// <exception cref="SerializationException">Thrown when the object fails to deserialize.</exception>
        public static TType BuildObject<TType>(XmlReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            return (TType)BuildObject(typeof(TType), reader);
        }
    }
}
